home *** CD-ROM | disk | FTP | other *** search
/ ftp.hitl.washington.edu / ftp.hitl.washington.edu.tar / ftp.hitl.washington.edu / pub / people / tsoper / CT Explorer / OpenGLSliceView.cs < prev    next >
Text File  |  2005-06-12  |  13KB  |  519 lines

  1. //This is a class containing an OpenGL view that handles all
  2. //appropriate rendering contexts for use of common OpenGL 
  3. //commands. 
  4.  
  5. //TO DO: Change name from SliceView to View
  6.  
  7. using System;
  8. using CsGL.OpenGL;
  9. using SampleGUI;
  10. using System.ComponentModel;
  11. using System.Drawing;
  12. using System.Collections;
  13. using System.Windows.Forms;
  14.  
  15.  
  16. //TAKE OUT
  17. public enum VIEW_TYPE
  18. {
  19.     SAGITTAL,    //view the yz plane
  20.     CORONAL,    //view the xz plane
  21.     TRANSVERSE,    //view the xy plane
  22.     OBLIQUE,    //non-standard view
  23. }
  24.  
  25. public enum DRAW_QUALITY
  26. {
  27.     LOW,    //draw pixel data using a bitmap and glDrawPixels
  28.     HIGH,    //draw gridded data smoothly using GL_QUADS
  29. }
  30.  
  31. public enum AXIS
  32. {
  33.     X,
  34.     Y,
  35.     Z
  36. }
  37.  
  38.  
  39. public class SliceView : OpenGLControl
  40. {
  41.     //Attributes//
  42.     private double[] volume = new double[3];    //3D working volume
  43.     private int[] planarAxes = new int[2];
  44.     private int orthoAxis;
  45.  
  46.     //TO DO: add scale and rotate factors that correlate 
  47.     //pixels moved to amount zoomed or rotated
  48.  
  49.     private bool mouseDownLeft;
  50.     private bool mouseDownRight;
  51.     private float xRot, yRot;
  52.     private Point mousePos;
  53.     private Point zoomFocus = new Point(0,0);
  54.     public bool IsRotatable;  //TO DO: Make this a property
  55.     private bool isRotated;
  56.     private bool isRotating = false;
  57.     private bool isZooming = false;
  58.     public bool IsZoomable; //TO DO: Make this a property
  59.     private float zoomScale = 1.0f;
  60.  
  61.     
  62.     private ElementIndexer3D indexer;
  63.     private ElementIndexer3D sizeIndexer;
  64.  
  65.     private SliceDrawer sliceDrawer = new SliceDrawer();
  66.  
  67.     //TAKE THIS OUT
  68.     public Label lbl = new Label();
  69.  
  70.     public Scan scan;
  71.     public Eye eye;
  72.     private bool scanLoaded;
  73.     public int sliceIndex;
  74.     public bool DisplayFromBuffer = false;
  75.     public bool WriteToBuffer = false;
  76.     private ushort[] pixelArray;
  77.     //public DRAW_QUALITY DrawQuality;
  78.     private int xidx, yidx, zidx; //connects axes with array indices
  79.  
  80.     //Properties
  81.     private VIEW_TYPE viewType;
  82.     public VIEW_TYPE ViewType
  83.     {
  84.         get
  85.         {
  86.             return viewType;
  87.         }
  88.         set
  89.         {
  90.             viewType = value;
  91.             AssignAxesToArrayIndices(); //assign x,y,z axes
  92.                                         //to [i,j,k] indices
  93.         }
  94.         
  95.     }
  96.  
  97.     private DRAW_QUALITY drawQuality;
  98.     public DRAW_QUALITY DrawQuality
  99.     {
  100.         get
  101.         {
  102.             return drawQuality;
  103.         }
  104.         set
  105.         {
  106.             drawQuality = value;
  107.             InitGLContext();
  108.             OnSizeChanged(null);
  109.             glDraw();
  110.             this.Refresh();
  111.         }
  112.     }
  113.  
  114.     
  115.     public float[] Color = new float[3];
  116.  
  117.     public SliceView()
  118.     {
  119.         scanLoaded = false;
  120.  
  121.         //set ortho view property
  122.         eye = new Eye();
  123.         viewType = VIEW_TYPE.TRANSVERSE; 
  124.         DrawQuality = DRAW_QUALITY.LOW;
  125.         //ViewType = VIEW_TYPE.ORTHO_2D;
  126.         volume[0] = volume[1] = volume[2] = 5;
  127.         AssignAxesToArrayIndices();
  128.         this.MouseDown += new MouseEventHandler( 
  129.                                     SliceView_MouseDown );
  130.         this.MouseUp += new MouseEventHandler(
  131.                                     SliceView_MouseUp);
  132.         this.MouseMove += new MouseEventHandler(
  133.                                     SliceView_MouseMove);
  134.         this.IsRotatable = true;
  135.  
  136.         xRot = yRot = 0;
  137.  
  138.         //create an indexer to convert between row-colum-plane to x-y-z
  139.         indexer = new ElementIndexer3D(AXIS_ALIGN.POSITIVE_Y,
  140.                     AXIS_ALIGN.POSITIVE_X,AXIS_ALIGN.NEGATIVE_Z);
  141.         sizeIndexer = new ElementIndexer3D(AXIS_ALIGN.POSITIVE_Y,
  142.                     AXIS_ALIGN.POSITIVE_X,AXIS_ALIGN.POSITIVE_Z);
  143.     }
  144.  
  145.     //TO DO: change LoadScan to LinkToScan or make a property
  146.     public void LoadScan(Scan s)
  147.     {
  148.         scan = s;
  149.         scanLoaded = true;
  150.         
  151.         //TO DO: make an easier way to recast arrays (might change to xyzposition class)
  152.         sizeIndexer.RCPIndex = scan.DimensionSize;
  153.         float[] startIndex = new float[3];
  154.         startIndex[0] = 0;
  155.         startIndex[1] = 0;
  156.         startIndex[2] = -sizeIndexer.Z;
  157.         eye.Index = startIndex;
  158.         int[] volume = new int[3];
  159.         volume = sizeIndexer.XYZIndex;
  160.         float[] volumef = new float[3];
  161.         for(int i = 0; i < 3; i++)
  162.             volumef[i] = (float) volume[i];
  163.         eye.Volume = volumef;
  164.         AssignAxesToArrayIndices();
  165.         eye.lbl = this.lbl;
  166.     }
  167.                                
  168.     public override void glDraw()
  169.     {
  170.         GL.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
  171.         GL.glShadeModel(GL.GL_SMOOTH);
  172.  
  173.         if(scanLoaded)
  174.         {
  175.             if(DrawQuality.Equals(DRAW_QUALITY.LOW))
  176.             {
  177.                 if(IsRotatable)
  178.                     DrawUnsmoothSlice((AXIS)viewType);
  179.                 else
  180.                     DrawUnsmooth();
  181.             }
  182.             else
  183.             {
  184.                 if(isRotated)
  185.                 {
  186.                     if(isRotating || isZooming)
  187.                     {
  188.                         DrawUnsmoothSlice((AXIS)VIEW_TYPE.TRANSVERSE);
  189.                         DrawUnsmoothSlice((AXIS)VIEW_TYPE.CORONAL);
  190.                         DrawUnsmoothSlice((AXIS)VIEW_TYPE.SAGITTAL);
  191.                     }
  192.                     else
  193.                     {
  194.                         DrawSmoothSlice((AXIS)VIEW_TYPE.TRANSVERSE);
  195.                         DrawSmoothSlice((AXIS)VIEW_TYPE.CORONAL);
  196.                         DrawSmoothSlice((AXIS)VIEW_TYPE.SAGITTAL);
  197.                     }
  198.                 }
  199.                 else
  200.                 {
  201.                     if(isZooming)
  202.                         DrawUnsmoothSlice((AXIS)viewType);
  203.                     else
  204.                         DrawSmoothSlice((AXIS)viewType);
  205.                 }
  206.             }
  207.         }
  208.     }
  209.  
  210.     protected override void InitGLContext()
  211.     {
  212.         GL.glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
  213.         GL.glEnable( GL.GL_DEPTH_TEST );
  214.     }
  215.     
  216.     protected override void OnSizeChanged(EventArgs e)
  217.     {
  218.         base.OnSizeChanged(e);
  219.     
  220.         //add this so that nothing happens when the window
  221.         //is minimized (i.e. Size = 0) b/c I get an error
  222.         //if(this.Size.Width.Equals(0) || this.Size.Height.Equals(0))
  223.         //    return;
  224.     }
  225.  
  226.     protected void AssignAxesToArrayIndices()
  227.     {
  228.         switch(viewType)
  229.         {
  230.             //TAKE OUT xidx yidx zidx
  231.             case VIEW_TYPE.TRANSVERSE:
  232.                 xidx = 1; yidx = 0; zidx = 2;
  233.                 eye.SetViewingSide(VIEWING_SIDE.TOP);
  234.                 break;
  235.             case VIEW_TYPE.SAGITTAL:
  236.                 xidx = 0; yidx = 2; zidx = 1;
  237.                 eye.SetViewingSide(VIEWING_SIDE.LEFT);
  238.                 break;
  239.             case VIEW_TYPE.CORONAL:
  240.                 xidx = 1; yidx = 2; zidx = 0;
  241.                 eye.SetViewingSide(VIEWING_SIDE.FRONT);
  242.                 break;
  243.             default:
  244.                 xidx = 1; yidx = 0; zidx = 2;
  245.                 break;
  246.         }
  247.         this.Refresh();
  248.     }
  249.  
  250.     //******** DRAWING FUNCTIONS*******************//
  251.     private void DrawUnsmooth()
  252.     {            
  253.         //GL.glDisable(GL.GL_DEPTH_TEST);
  254.         //GL.glMatrixMode(GL.GL_PROJECTION);
  255.         GL.glLoadIdentity();
  256.         GL.gluOrtho2D(0 , this.Width, 0, this.Height);
  257.         GL.glMatrixMode( GL.GL_MODELVIEW );
  258.         GL.glLoadIdentity();
  259.  
  260.         int dimX = scan.DimensionSize[xidx];
  261.         int dimY = scan.DimensionSize[yidx];
  262.         int[] arrayIdx = new int[3];
  263.         arrayIdx[zidx] = scan.SliceIndex[zidx];
  264.         ushort[] pa = new ushort[dimX*dimY*3];
  265.         for(int i = 0; i < dimY; i++)
  266.         {
  267.             for(int j = 0; j < dimX; j++)
  268.             {
  269.                 arrayIdx[xidx] = j;
  270.                 arrayIdx[yidx] = i;
  271.                 pa[(i*dimX+j)*3] = scan.Data[arrayIdx[0],arrayIdx[1],arrayIdx[2]];
  272.                 pa[(i*dimX+j)*3+1] = scan.Data[arrayIdx[0],arrayIdx[1],arrayIdx[2]];
  273.                 pa[(i*dimX+j)*3+2] = scan.Data[arrayIdx[0],arrayIdx[1],arrayIdx[2]];
  274.             }
  275.         }
  276.  
  277.         int window = scan.Window;
  278.         int level = scan.Level;
  279.  
  280.         //scale color components
  281.         GL.glPixelTransferf(GL.GL_RED_BIAS,-(level-window/2)/
  282.             (float)window);
  283.         GL.glPixelTransferf(GL.GL_GREEN_BIAS,-(level-window/2)/
  284.             (float)window);
  285.         GL.glPixelTransferf(GL.GL_BLUE_BIAS,-(level-window/2)/
  286.             (float)window);
  287.         GL.glPixelTransferi(GL.GL_RED_SCALE,65536/window);
  288.         GL.glPixelTransferi(GL.GL_GREEN_SCALE,65536/window);
  289.         GL.glPixelTransferi(GL.GL_BLUE_SCALE,65536/window);
  290.  
  291.         //unpack so that image dimensions
  292.         //do NOT have to be multiple of 4
  293.         GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT,1);
  294.  
  295.         //zoom so that image takes up entire viewport
  296.         float zoomx, zoomy;
  297.         zoomx = this.Size.Width/(float)dimX;
  298.         zoomy = this.Size.Height/(float)dimY;
  299.         
  300.         //set raster position
  301.         switch(this.viewType)
  302.         {
  303.             case VIEW_TYPE.TRANSVERSE:
  304.                 GL.glRasterPos2f(-1f,-1f);
  305.                 break;
  306.             case VIEW_TYPE.SAGITTAL:
  307.                 zoomy *= -1;//flip sagittal y view
  308.                 zoomx *= -1;//flip sagittal x view
  309.                 GL.glRasterPos2f(1f,1f);
  310.                 break;
  311.             case VIEW_TYPE.CORONAL:
  312.                 zoomy *= -1;//flip coronal y view
  313.                 GL.glRasterPos2f(-1f,1f);
  314.                 break;
  315.             default: break;
  316.         }
  317.             
  318.         //set zoom
  319.         GL.glPixelZoom(zoomx,zoomy);
  320.             
  321.         //Draw from the buffer
  322.         GL.glDrawPixels(dimX,dimY, GL.GL_RGB, GL.GL_UNSIGNED_SHORT,pa);
  323.     
  324.         GL.glFlush();
  325.     }
  326.  
  327.     private void RotateScene()
  328.     {
  329.         if(this.IsRotatable && this.isRotated)
  330.         {    
  331.             //get coordinates of last element
  332.             indexer.RCPIndex = scan.DimensionSize;
  333.             
  334.             //back translate
  335.             GL.glTranslatef(indexer.X/2.0f,indexer.Y/2.0f,indexer.Z/2.0f);
  336.  
  337.             //rotate in x relative to camera
  338.             GL.glRotatef(xRot,eye.UpVector[0],eye.UpVector[1],
  339.                 eye.UpVector[2]);
  340.  
  341.             //rotate in y relative to camera
  342.             GL.glRotatef(yRot,eye.RightVector[0],eye.RightVector[1],
  343.                 eye.RightVector[2]);
  344.  
  345.             //forward translate
  346.             GL.glTranslatef(-indexer.X/2.0f,-indexer.Y/2.0f,-indexer.Z/2.0f);
  347.         }
  348.     }
  349.  
  350.     private void DrawSmoothSlice(AXIS normalAxis)
  351.     {
  352.         sliceDrawer.Indexer = indexer; //TO DO: PUT THIS IN LOAD SCAN
  353.         eye.SetView();
  354.         this.RotateScene();
  355.         sliceDrawer.DrawSlice(scan,(int)normalAxis);
  356.     }
  357.  
  358.     private void DrawUnsmoothSlice(AXIS normalAxis)
  359.     {
  360.         //define axis that are coplanar with the slice
  361.         AXIS[] planarAxes = new AXIS[2];
  362.         int idx = 0;
  363.         for(int i = 0; i < 3; i++)
  364.         {
  365.             if(i != (int)normalAxis)
  366.             {
  367.                 planarAxes[idx] = (AXIS)i;
  368.                 idx++;
  369.             }
  370.         }
  371.         
  372.         //get size in x,y,z
  373.         sizeIndexer.RCPIndex = scan.DimensionSize;
  374.         indexer.RCPIndex = scan.DimensionSize;
  375.         int dim1 = sizeIndexer.GetXYZElement((int)planarAxes[0]);
  376.         int dim2 = sizeIndexer.GetXYZElement((int)planarAxes[1]);
  377.  
  378.         float gray;
  379.         int window = scan.Window;
  380.         int level = scan.Level;
  381.  
  382.         GL.glEnable(GL.GL_DEPTH_TEST);
  383.         GL.glMatrixMode(GL.GL_MODELVIEW);
  384.         GL.glLoadIdentity();
  385.         GL.glOrtho(eye.Left,eye.Right,eye.Bottom,eye.Top,
  386.             eye.Near,eye.Far);
  387.         GL.gluLookAt(eye.Position[0],eye.Position[1],eye.Position[2],
  388.             eye.Target[0],eye.Target[1],eye.Target[2],
  389.             eye.UpVector[0],eye.UpVector[1],eye.UpVector[2]);
  390.  
  391.         //rotate
  392.         if(this.IsRotatable && this.isRotated)
  393.         {    
  394.             //find the location of the last element so we can translate to the center
  395.             indexer.RCPIndex = scan.DimensionSize;
  396.  
  397.             //back translate
  398.             GL.glTranslatef(indexer.X/2.0f,indexer.Y/2.0f,indexer.Z/2.0f);
  399.  
  400.             //rotate in x relative to camera
  401.             GL.glRotatef(xRot,eye.UpVector[0],eye.UpVector[1],
  402.                 eye.UpVector[2]);
  403.  
  404.             //rotate in y relative to camera
  405.             GL.glRotatef(yRot,eye.RightVector[0],eye.RightVector[1],
  406.                 eye.RightVector[2]);
  407.  
  408.             //forward translate
  409.             GL.glTranslatef(-indexer.X/2.0f,-indexer.Y/2.0f,-indexer.Z/2.0f);
  410.         }
  411.         
  412.         indexer.RCPIndex = scan.SliceIndex;
  413.         GL.glPointSize(this.Width/250f*zoomScale*3f);
  414.         for(int i = 0; i < dim1-1; i++)
  415.         {
  416.             for(int j = 0; j < dim2-1; j++)
  417.             {
  418.                 GL.glBegin(GL.GL_POINTS);
  419.  
  420.                 indexer.SetXYZElement((int)planarAxes[0],i);
  421.                 indexer.SetXYZElement((int)planarAxes[1],j);
  422.                 gray = scan.Data[indexer.Row,indexer.Column,indexer.Plane];
  423.                 gray = (gray-(level-window/2))/(float)window;
  424.                 GL.glColor3f(gray,gray,gray);
  425.                 GL.glVertex3f(indexer.X,indexer.Y,indexer.Z);
  426.  
  427.                 GL.glEnd();
  428.             }
  429.         }
  430.     }
  431.  
  432.     //EVENT HANDLING------------------------------------------------------------//
  433.     //handle mouse down event
  434.     //TO DO: add mouse window and level 
  435.     private void SliceView_MouseDown( object sender, MouseEventArgs e)
  436.     {
  437.         if(e.Button.Equals(MouseButtons.Left) && IsRotatable)
  438.         {
  439.             mouseDownLeft = true;
  440.             mousePos.X = e.X;
  441.             mousePos.Y = e.Y;
  442.         }
  443.         if(e.Button.Equals(MouseButtons.Right) && IsZoomable)
  444.         {
  445.             mouseDownRight = true;
  446.             mousePos.X = e.X;    //record mouse pos so we can measure distance moved
  447.             mousePos.Y = e.Y;    //record mouse pos so we can measure distance moved
  448.  
  449.             //also record this point as the focus of the zoom
  450.             float[] pos = new float[2];
  451.             pos[0] = e.X/(float)this.Width*(eye.Right - eye.Left) + eye.Left; //x zoom focus
  452.             pos[1] = e.Y/(float)this.Height*(eye.Bottom - eye.Top) + eye.Top; //y zoom focus
  453.             eye.ZoomFocus = pos;
  454.             eye.SetZoom(zoomScale);
  455.         }
  456.     }
  457.  
  458.     //handle mouse up event
  459.     private void SliceView_MouseUp( object sender, MouseEventArgs e)
  460.     {
  461.         mouseDownLeft = false;
  462.         mouseDownRight = false;
  463.         if(isRotating)
  464.         {
  465.             isRotating = false;
  466.             this.Refresh();
  467.         }
  468.         if(isZooming)
  469.         {
  470.             isZooming = false;
  471.             this.Refresh();
  472.         }
  473.  
  474.     }
  475.  
  476.     //handle mouse move event
  477.     private void SliceView_MouseMove( object sender, MouseEventArgs e)
  478.     {
  479.         if(IsRotatable && mouseDownLeft)
  480.         {
  481.             isRotated = true;
  482.             isRotating = true;
  483.             xRot += e.X - mousePos.X;
  484.             yRot += e.Y - mousePos.Y;
  485.             if(xRot > 360)
  486.                 xRot -= 360;
  487.             if(xRot < -360)
  488.                 xRot += 360;
  489.             if(yRot > 360)
  490.                 yRot -= 360;
  491.             if(yRot < -360)
  492.                 yRot += 360;
  493.             
  494.             mousePos.X = e.X;
  495.             mousePos.Y = e.Y;
  496.             this.Refresh();
  497.         }
  498.         if(IsZoomable && mouseDownRight)
  499.         {
  500.             isZooming = true;
  501.  
  502.             if(e.Y < mousePos.Y)
  503.                 zoomScale += 1f*(float)Math.Pow((e.Y - mousePos.Y),1)/(float)this.Height;
  504.             else
  505.                 zoomScale += 1f*(float)Math.Pow((e.Y - mousePos.Y),1)/(float)this.Height;
  506.             
  507.             mousePos.X = e.X;
  508.             mousePos.Y = e.Y;
  509.             eye.SetZoom( zoomScale );
  510.             zoomScale = eye.ZoomScale; //in case we've exceeded the min or max
  511.             this.Refresh();
  512.  
  513.             int[] tmp = {5,5,5};
  514.             eye.Volume = tmp;
  515.         }
  516.     }
  517. }
  518.  
  519.